# coding=utf-8

import json
import struct

from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory
from firefly.server.globalobject import GlobalObject, remoteserviceHandle, webserviceHandle
from firefly.utils.services import CommandService
from twisted.internet import defer, reactor
from twisted.python import log
from twisted.web import resource

from game.share import EncodeUtil

CONNECTION_COUNT = 0
DISCONNECT_CLIENT_INTERVAL = 0.5
CLOSE_TIME_OUT = 4000  # 心跳超时
CLOSE_NET_FULL = 4001  # webnet 链接已满 默认最大链接数为300
CLOSE_COVER = 4002  # 玩家被挤下去

ROUTE = {"value": {}, "range": {}}

remote_name = GlobalObject().json_config.get("route_server")
server_name = GlobalObject().json_config.get("name", "net")
port = GlobalObject().json_config.get("port", 10000)
MAX_CONNECTS = GlobalObject().json_config.get("max_connects", 300)
encode_type = GlobalObject().json_config.get("encode_type", 0)
if not __name__ == "__main__":
    with open("config.json", 'r') as f:
        config = json.load(f)
        ROUTE = config.get("route")


def defferedErrorHandle(e):
    """
    延时对象的错误处理
    :param e:
    :return:
    """
    log.err(str(e))


class Datapack:
    def __init__(self):
        self.handfrt = 'iii'
        self.version = 0

    def _get_head_len(self):
        return struct.calcsize(self.handfrt)

    def _unpack(self, dpack):
        if len(dpack) < self._get_head_len():
            return None
        dhead = dpack[0: self._get_head_len()]
        lhead = struct.unpack(self.handfrt, dhead)
        data = dpack[self._get_head_len():]
        data = EncodeUtil.decode(data, encode_type)
        if data is None:
            return None

        return {"result": True, "command": lhead[1], "data": data}

    def _pack(self, response, command):
        """
        消息打包
        :param response:
        :param command:
        :return:
        """
        tmp = response
        response = EncodeUtil.encode(str(response), encode_type)
        length = response.__len__() + self._get_head_len()
        command_id = command

        data = struct.pack(self.handfrt, length, command_id, 0)
        data += response

        return data


class NetCommandService(CommandService):
    def callTargetSingle(self, targetKey, *args, **kw):

        self._lock.acquire()
        try:
            target = self.getTarget(0)
            if not target:
                print "targetkey=", targetKey
                log.err('the command' + str(targetKey) + "not Found on service")
            if targetKey not in self.unDisplay:
                pass
            defer_data = target(targetKey, *args, **kw)
            if not defer_data:
                return None
            if isinstance(defer_data, defer.Deferred):
                return defer_data
            d = defer.Deferred()
            d.callback(defer_data)
        finally:
            self._lock.release()
        return d


netservice = NetCommandService("WebnetService")


class WebSocketProtocol(WebSocketServerProtocol):
    def __init__(self):
        WebSocketServerProtocol.__init__(self)
        self.time_out_task = None
        self.connect_timeout = 40 * 60

    def add_new_time_task(self):
        if self.connect_timeout > 0:
            self.close_time_task()
            self.time_out_task = reactor.callLater(self.connect_timeout, self.sendClose, CLOSE_TIME_OUT,
                                                   "timeout".decode())

    def close_time_task(self):
        if self.time_out_task and self.time_out_task.active():
            self.time_out_task.cancel()
            self.time_out_task = None

    def onConnect(self, request):
        log.err("{1} client connecting ; {0}".format(request.peer, self.transport.sessionno))

        global CONNECTION_COUNT
        CONNECTION_COUNT += 1
        if CONNECTION_COUNT > MAX_CONNECTS:
            reactor.callLater(DISCONNECT_CLIENT_INTERVAL, self.sendClose, CLOSE_NET_FULL, "connect is full".decode())

        self.factory._clients[self.transport.sessionno] = self
        self.add_new_time_task()

    def onOpen(self):
        print ("webSocket open!!")

    def onMessage(self, payload, isBinary):
        if isBinary:
            print("Binary message received: {0} bytes".format(len(payload)))
        else:
            print("Text message received: {0}".format(payload.decode('utf8')))

        # ret = self.factory._datapack._unpack(payload)
        ret = payload.decode('utf8')
        if ret:
            self.add_new_time_task()
            d = netservice.callTarget(ret["command"], self, ret["data"])
            if d:
                d.addCallback(self.send_data_for_me, ret["command"])
                d.addErrback(defferedErrorHandle)

    def onClose(self, wasClean, code, reason):
        log.msg("WebSocket connection closed: {0}".format(reason))
        global CONNECTION_COUNT
        CONNECTION_COUNT += 1
        self.close_time_task()
        self.factory.onClose(self)

    def send_data_for_me(self, data, command_id):
        if data is None:
            return
        send_data = self.factory.produc_result_for_me(command_id, data)
        self.sendMessage(send_data, isBinary=1)


class CustomWebNetFactory(WebSocketServerFactory):
    protocol = WebSocketProtocol

    def __init__(self):
        super(CustomWebNetFactory, self).__init__()
        self._clients = {}
        self._datapack = Datapack()

    def build_protocol(self):
        p = self.protocol()
        p.factory = self
        return p

    def pushobject(self, sendList, data, isBinary):
        for sid, client in self._clients.items():
            if sid in sendList:
                client.sendMessage(data, isBinary)

    def onClose(self, close_clinet):
        for sid, clinet in self._clients.items():
            if sid == close_clinet.transport.sessionno:
                session_id = "%s,%d" % (server_name, sid)
                GlobalObject().remote[remote_name].callRemote("netconnlost", session_id)
                self._clients.pop(sid)
                break

    def produce_result_for_me(self, command, data):
        if data is None:
            data = {}
        send_data = self._datapack._pack(data, command)
        return send_data


websocket_factory = CustomWebNetFactory()

print "start listen post=", port
reactor.listenTCP(port, websocket_factory)


def netserviceHandle(target):
    netservice.mapTarget(target)


@netserviceHandle
def Forwarding_0(keyname, _conn, data):
    print "Forwarding_0!!"
    print "keyname=", keyname
    print "data=", data
    uid = "%s,%d" % (server_name, _conn.transport.sessionno)
    route_to_server = ""
    str_key = str(keyname)
    if ROUTE["value"].has_key(str_key):
        route_to_server = ROUTE["value"][str_key]
    else:
        for k, v in ROUTE["range"].items():
            flag = False
            for r in v:
                if r[0] <= keyname and r[1] >= keyname:
                    route_to_server = k
                    flag = True
                    break
            if flag:
                break
    print "Forwarding_0!! route_to_server=",route_to_server
    if "" == route_to_server:
        print u"没有匹配的命令ID"
        # 没有匹配到的规则

    # test return
    rv = json.dumps({"code": 100003, "ret": 1, "info": {}})
    GlobalObject().remote["gate"].callRemote("forwarding_root", keyname, uid, data)
    print "change!"
    return GlobalObject().remote["gate"].callRemote("forwarding_root", keyname, uid, data)

print "GlobalObject().remote =", GlobalObject().remote

@remoteserviceHandle(remote_name)
def pushObject(msgid, msg, sendList):
    print "pushObject msgid=%s, msg=%s" % (msgid, msg)
    data = websocket_factory._datapack._pack(msg, msgid)
    websocket_factory.pushobject(sendList, data, True)


@netserviceHandle
def net_handle_200(_conn, data):
    """
    所有 200 的消息进来后由我处理
    :param _conn:
    :param data:
    :return:
    """
    print 'net redirect gate!!!'
    return GlobalObject().remote['gate'].callRemote('gate_handle_200', data)  # 在此将消息转发给我的父亲 gate


@webserviceHandle("test/web")
class PushMessage(resource.Resource):
    def render(self, request):
        print "clients = ", websocket_factory._clients
        # try:
        command_id = request.args.get("command_id", None)
        msg = request.args.get("msg", None)
        send_list = request.args.get("send_list", [])
        print request.args
        if not msg or not send_list:
            return "Param error!"
        # payload = websocket_factory._datapack._pack(msg[0], command_id[0])
        payload = bytes(msg)
        send_list = [0]
        websocket_factory.pushobject(send_list, payload, True)
        return "ok"
        # except Exception, e:
        #     print e
        #     return "error"


@remoteserviceHandle("gate")
def push_object(key, send_list, data={}):
    print "webnet_1, push_object key=", key
    log.msg("webnet_1, push_object key=%s" % key)
    websocket_factory.pushobject(send_list, data, True)

if __name__ == "__main__":
    print "test!!!"
    print "pushObject!!!"
    pushObject(100999, "fff", [1])
    reactor.run()
